#include "controlBot.h"
#include <iostream>
#include <windows.h>
#include <cmath> 
#include <math.h>
#include <iosfwd>

//////////////////////////////////////////////////////////////////////////
//Author	:	Ross Conroy	ross.conroy@tees.ac.uk
//Date		:	06/05/2014
//
//This class controls a single bot/agent, each time step it makes an
//observation of whether the enemy is visible and then takes actions
//by expanding an influence diagram into a policy tree to determine what
//action the agent should take based on the current observation and
//time step.
//
//Update 1
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	13/05/2014
//Updated to use new decision tree/policy parser
//
//Update 2
//Author	:	Ross Conroy ross.cornoy@tees.ac.uk
//Date		:	22/05/2014
//Updated escape to determine direction away from enemy to escape to
//
//Update 3
//Author	:	Ross Conroy	ross.conroy@tees.ac.uk
//Date		:	23/05/2014
//updated to set settings from command line arguments
//
//Update 4
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	10/06/2014
//Updated to work with noting actions of enemy agent to determine the
//what policy the agent is following
//////////////////////////////////////////////////////////////////////////

ControlBot::ControlBot(PolicyFileParser * parser,  int maxSteps, int timeStepDuration, string outputCsvFile)
{
	_agent = *Broodwar->self()->getUnits().begin();
	//_agent = * Broodwar->enemy()->getUnits().begin();
	_currentStep = 1;
	_timeSinceLastStep = 0;
	_timeBetweenSteps = timeStepDuration;

	_decisionTreeParser = parser;
	_maxSteps = maxSteps;

	_escapeDistance = 55;
	_outputCsvFIle = outputCsvFile;
	//_lastKnownEnemyPosition;
}

//Gets the time difference between each tick of the game clock
float diffclock(clock_t clock1, clock_t clock2)
{
	float diffticks=clock2-clock1;
	float diffms=(diffticks*1000)/CLOCKS_PER_SEC;
	return diffms;
}

//Tick, each clock cycle in game
void ControlBot::Tick()
{
	//_agent = *Broodwar->self()->getUnits().begin();
	float deltaTime = diffclock(_lastTime, clock());
	_lastTime = clock();
	_timeSinceLastStep += deltaTime;

	if(_timeSinceLastStep >= _timeBetweenSteps)
	{
		_timeSinceLastStep = 0;				
		MakeDecision();
		if(_outputCsvFIle != "")
		{
			SaveCsvTimeStep();
		}
		_currentStep++;

		if(_currentStep > _maxSteps)
		{
			_currentStep = 1;
		}
	}
	else
	{
		if(_lastAction == "Attack")
		{
			Attack();
		}
		else if(_lastAction == "Escape")
		{
			Escape();
		}
	}
	//cout << "Remaining Health: " << _agent->getHitPoints() << endl;
}

//Used the hugin API to determine if the agent should attack or escape
void ControlBot::MakeDecision()
{
	string action;

	if(CanSeeEnemy())
	{
		_lastObservation = "See";
		Unit * closestEnemy = GetNearestEnemy();

		if(closestEnemy->isAttacking() || closestEnemy->isAttackFrame())
		{
			action = _decisionTreeParser->DecisionFromObservation(_currentStep, "See", "Attack");
		}
		else
		{
			action = _decisionTreeParser->DecisionFromObservation(_currentStep, "See", "Escape");
		}		
	}
	else
	{
		_lastObservation = "DontSee";
		action = _decisionTreeParser->DecisionFromObservation(_currentStep, "DontSee", "Escape");
	}

	if(action == "Attack")
	{
		Attack();
	}
	else if(action == "Escape")
	{
		Escape();
	}
	else
	{
		Broodwar->printf("Invalid Decision ");
	}
	_lastAction = action;
}

//actions to attack
//attacking is simply finding the nearest enemy then attacking it
//ToDo fix why agent not attacking enemy
void ControlBot::Attack()
{
	try
	{
		Broodwar->printf("Attack");

		Unit* closestEnemy=NULL;
		for(std::set<Unit*>::const_iterator m = Broodwar->enemy()->getUnits().begin(); m!=Broodwar->enemy()->getUnits().end(); m++)
		{
			if (closestEnemy==NULL || (_agent)->getDistance(*m)<(_agent)->getDistance(closestEnemy))
				closestEnemy=*m;
		}

		if(closestEnemy != NULL)
		{
			/*cout << "weapon range" << _agent->isInWeaponRange(closestEnemy) << endl;
			cout << "attack frame" << _agent->isAttackFrame() << endl;
			cout << "is attacking" << _agent->isAttacking() << endl;

			if(_agent->isInWeaponRange(closestEnemy))
			{				
			if(!_agent->isAttackFrame())
			{
			_agent->attack(closestEnemy, false);
			}					
			}
			else
			{
			_agent->move(_lastKnownEnemyPosition);
			}*/
			_agent->attack(closestEnemy->getPosition(), false);
		}
		else
		{
			//check for valid position
			if(_lastKnownEnemyPosition.x() > 0 && _lastKnownEnemyPosition.y() > 0)
			{
				_agent->move(_lastKnownEnemyPosition);
			}
			Broodwar->printf("No Enemy Available");
		}
	}
	catch(exception e)
	{
		cout << e.what() << endl;
	}	
}

//Actions to escape
//Escaping is simply moving away from the enemy agent
void ControlBot::Escape()
{
	try
	{
		Broodwar->printf("Escape");

		Unit* closestEnemy=NULL;
		for(std::set<Unit*>::const_iterator m = Broodwar->enemy()->getUnits().begin(); m!=Broodwar->enemy()->getUnits().end(); m++)
		{
			if (closestEnemy==NULL || (_agent)->getDistance(*m)<(_agent)->getDistance(closestEnemy))
				closestEnemy=*m;
		}

		if(closestEnemy != NULL)
		{
			Position enemyPos = closestEnemy->getPosition();
			Position unitPos = _agent->getPosition();
			int diffX = enemyPos.x() - unitPos.x();
			int diffY = enemyPos.y() - unitPos.y();
			float tmp = (diffX*diffX) + (diffY*diffY);
			float dist = sqrt(tmp);
			float multi = _escapeDistance / dist;

			diffX = diffX * -1 * multi;
			diffY = diffY * -1 * multi;

			Position newPos;
			newPos.x() = unitPos.x() + diffX;
			newPos.y() = unitPos.y() + diffY;

			//pos.x() = pos.x() + 100;
			//pos.y() = pos.y() + 100;
			//_agent->move(newPos);
			_agent->rightClick(newPos, false);
			//_agent->setRallyPoint(newPos);
		}
	}
	catch(exception e)
	{
		cout << e.what() << endl;
	}
}

//Determines whether an enemy is visible
bool ControlBot::CanSeeEnemy()
{	
	try
	{
		Unit* closestEnemy=GetNearestEnemy();
		/*for(std::set<Unit*>::const_iterator m = Broodwar->enemy()->getUnits().begin(); m!=Broodwar->enemy()->getUnits().end(); m++)
		{
		if (closestEnemy==NULL || (_agent)->getDistance(*m)<(_agent)->getDistance(closestEnemy))
		closestEnemy=*m;
		}*/

		//Unit * enemy = *Broodwar->enemy()->getUnits().begin();

		if(closestEnemy != NULL)
		{				
			if(closestEnemy->isVisible())
			{
				Broodwar->printf("Enemy Visible");
				_lastKnownEnemyPosition = closestEnemy->getPosition();
			}
			else
			{
				Broodwar->printf("Enemy Invisible");
			}
			return closestEnemy->isVisible();
		}
		else
		{
			Broodwar->printf("No Enemies");
			return false;
		}
	}
	catch(exception e)
	{
		cout << e.what() << endl;
		return false;
	}	
}

//Gets the nearest enemy
Unit * ControlBot::GetNearestEnemy()
{
	Unit* closestEnemy=NULL;
	for(std::set<Unit*>::const_iterator m = Broodwar->enemy()->getUnits().begin(); m!=Broodwar->enemy()->getUnits().end(); m++)
	{
		if (closestEnemy==NULL || (_agent)->getDistance(*m)<(_agent)->getDistance(closestEnemy))
			closestEnemy=*m;
	}

	return closestEnemy;
}

//Saves the last time step to csv file
void ControlBot::SaveCsvTimeStep()
{
	//Check file exists, if yes create new file
	//else append existing file	

	Unit * nearestEnemy = GetNearestEnemy();	
	stringstream ss;
	ss << clock() << "," << _currentStep << "," << _decisionTreeParser->GetCurrDirectory() << "," << _agent->isUnderAttack() << "," << _lastObservation << "," << _lastAction << "," << _agent->getHitPoints() << ",";

	if(nearestEnemy != NULL)
	{
		ss << GetNearestEnemy()->getHitPoints() << ",";
		ss << _agent->getHitPoints() - GetNearestEnemy()->getHitPoints() << ",";
	}

	string line = ss.str();
	_csvRows.push_back(line);

	if(_currentStep == _maxSteps)
	//if(true)
	{
		bool exists = false;
		ifstream f(_outputCsvFIle.c_str());
		if (f.good()) 
		{
			exists =  true;
		}
		f.close();

		//ofstream out(fileName.c_str());
		ofstream out;
		out.open (_outputCsvFIle.c_str(), std::ofstream::out | std::ofstream::app);

		if(!exists)
		{
			string colHeaders = "Time,TimeStep,Model,State,Observation,Action,Health,EnemyHealth,Difference";
			out << colHeaders << endl;
		}

		for(list<string>::iterator it = _csvRows.begin(); it != _csvRows.end(); it++)
		{
			string temp = *it;
			out << temp << endl;
		}		

		out.close();
		_csvRows.clear();
	}
}
